/*
 * Created on Sep 25, 2003
 * Arbiter
 * Local Projects Database
 * (c) 2003 eRomania Gateway Association
 *
 */
package ro.gateway.aida.usr;

import java.sql.Date;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import ro.gateway.aida.db.DBPersistenceManager;
import ro.gateway.aida.db.PersistenceToken;
import ro.gateway.aida.usr.messaging.Alert;
import ro.gateway.aida.usr.messaging.Approve;
import ro.gateway.aida.usr.messaging.ApproveDB;
import ro.gateway.aida.usr.messaging.Event;
import ro.gateway.aida.usr.messaging.EventDB;


/**
 * @author Mihai Postelnicu<p>
 *
 *  e-mail (mihai@ro-gateway.org)<br>
 * (c) 2003 by eRomania Gateway<p>
 *
 * This class takes decisions based on user actions
 */
public class Arbiter extends DBPersistenceManager {
    private Arbiter(PersistenceToken token) {
        super(token);
    }

    public static Arbiter getManager(PersistenceToken token) {
        return new Arbiter(token);
    }

    /**
     * Produces a set of users that were found in the given set of groups,
     * and that have/not have a role assigned that can be found in the Set roles.
     * The condition is given by the exclude parameter.
     * @param groups the groups to search users in
     * @param roles the roles to search users with
     * @param exclude true if we want the users that have one of the roles in roles, false otherwise
     * @return the set of users found
     * @throws SQLException
     */
    public Set getGroupsUsers(Set groups, SortedSet roles, boolean exclude)
        throws SQLException {
        TreeSet users = new TreeSet(new User.UserComparator());
        UserGroupDB ugdb = UserGroupDB.getManager(token);
        Iterator it = groups.iterator();
        GroupRole.GroupRoleComparator c = new GroupRole.GroupRoleComparator();

        while (it.hasNext()) {
            TreeSet tr = new TreeSet(roles);

            UserGroup g = (UserGroup) it.next();
            tr.add(g.getRole());

            if ((roles.size() == tr.size()) && !exclude) {
                Vector v = ugdb.getUsers(g.getId());
                users.addAll(v); //no duplicates allowed
            } else if ((roles.size() != tr.size()) && exclude) {
                Vector v = ugdb.getUsers(g.getId());
                users.addAll(v); //no duplicates allowed
            }
        }

        return users;
    }

    /**
     * Gets the users found in the set of groups
     * @param groups the set of groups
     * @return the set of users
     * @throws SQLException
     */
    public SortedSet getGroupsUsers(Set groups) throws SQLException {
        TreeSet users = new TreeSet(new User.UserComparator());
        UserGroupDB ugdb = UserGroupDB.getManager(token);
        Iterator it = groups.iterator();
        GroupRole.GroupRoleComparator c = new GroupRole.GroupRoleComparator();

        while (it.hasNext()) {
            UserGroup g = (UserGroup) it.next();
            Vector v = ugdb.getUsers(g.getId());
            users.addAll(v); //no duplicates allowed
        }

        return users;
    }

    /**
     * Creates a piece of information about an important event. Recors a log.
     * @param user_id the user that performed the action
     * @param obj_id the object that was the target of the action
     * @param obj_type the object type
     * @param event_type the type of event
     * @see Event
     * @return the generated event object that was recorded in db
     */
    public Event createEvent(long user_id, long obj_id, int obj_type,
        int event_type) {
        Event l = new Event();
        EventDB ldb = EventDB.getManager(token);
        l.setSourceUserId(user_id);
        l.setSent(new Date(System.currentTimeMillis()));
        l.setSourceType(obj_type);
        l.setSourceObjId(obj_id);
        l.setEventType(event_type);

        try {
            l.setEventId(ldb.add(l));
        } catch (Exception e) {
            //TODO: need a request object
            e.printStackTrace();
        }

        return l;
    }

    /**
     * This method will create approval alerts for the administrators
     * @param user_id
     * @param obj_id
     * @param obj_type
     * @param l
     * @throws SQLException
     */
    public void deployApprovals(long user_id, long obj_id, int obj_type, Event l)
        throws SQLException {
        if (obj_type != PROJECT_TYPE)
            return;

        ApproveDB apdb = ApproveDB.getManager(token);
        UserGroupDB ugdb = UserGroupDB.getManager(token);

        UserGroup ug = ugdb.getGroupForUser(user_id);
        GroupRole gr = ug.getRole();

        if (gr.canDo(Perms.AUTO_APPROVE)) {
            if (obj_type == PROJECT_TYPE)
                apdb.setApproveActivity(true, obj_id);
        } else {
            //track user supergroups for this user_id
            SortedSet groups = ug.getSuperGroups();

            SortedSet users = getGroupsUsers(groups);
            Iterator i = users.iterator();

            while (i.hasNext()) {
                User u = (User) i.next();
                Approve ap = new Approve(l, null);
                ap.setPriority(3);
                ap.setByEmail(false);
                ap.setAlertType(Alert.APPROVE_ALERT);
                ap.setEventType(Event.ALERT);
                ap.setDeleted(false);
                ap.setRead(null);
                ap.setReason(null);
                ap.setApproved(false);
                ap.setFeedback(0);
                ap.setTo((int) u.getId());
                apdb.add(ap);
            }
        }
    }

    public GroupRole getRole(HttpServletRequest request, long user_id)
        throws SQLException {
        if (user_id == 0)
            return new GuestRole();

        //check if the role is already in the session :
        HttpSession session = request.getSession();
        GroupRole gr = (GroupRole) session.getAttribute(GroupRole.CURRENT);

        if (gr == null) {
            UserGroupDB ugdb = UserGroupDB.getManager(token);
            UserGroup ug = ugdb.getGroupForUser(user_id);

            if (ug == null)
                gr = new GuestRole();
            else
                gr = ug.getRole();

            session.setAttribute(GroupRole.CURRENT, gr);
        }

        return gr;
    }

    public boolean canDo(HttpServletRequest request, int action, long obj_id,
        int obj_type) {
        User user = UserUtils.getLogged(request);

        if (user == null)
            return false;

        long user_id = user.getId();

        try {
            GroupRole role = getRole(request, user_id);
            UserDB udb = UserDB.getManager(token);
            long owner = udb.getOwner(obj_id, obj_type);

            //if the user owns the object, he can edit it:
            if (owner == user_id)
                return true;

            //if not, get subgroups, search if the owner is a subuser
            UserGroupDB ugdb = UserGroupDB.getManager(token);
            UserGroup ug = ugdb.getGroupForUser(user_id);

            if (ug == null)
                return false;

            Set sg = ugdb.getSubGroups(ug.getId());
            Set users = getGroupsUsers(sg);
            Iterator i = users.iterator();

            while (i.hasNext()) {
                User element = (User) i.next();

                if (element.getId() == owner)
                    return true;
            }

            //maybe he has rights to EDIT_OBJECT, or he is an admin
            if (role.canDo(action))
                return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return false;
    }

    public boolean canDo(HttpServletRequest request, int action) {
        User user = UserUtils.getLogged(request);

        if (user == null)
            return false;

        long user_id = user.getId();

        try {
            GroupRole role = getRole(request, user_id);

            if (role.canDo(action))
                return true;
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return false;
    }

    public static final int PROJECT_TYPE = 1;
    public static final int ORG_TYPE = 2;
    public static final int PERSON_TYPE = 3;
}
